library(rvest)
library(dplyr)
library(tidyverse)
library(plotly)

Function to get NBA roster for a specified year

get_nba_roster <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)


  # Extract the table containing the player statistics
  roster_table <- webpage %>%
    html_node("table#per_game_stats") %>%
    html_table(fill = TRUE)
  
  roster_table <- roster_table[-which(roster_table$Player=='League Average'),]
  # Clean the data (remove header rows that might be duplicated)
  roster_table <- roster_table %>%
    filter(Player != "Player")
    return(roster_table)
}

# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)
  
  # Extract the table containing the advanced player statistics
  advanced_stats_table <- webpage %>%
    html_node("table#advanced_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
 # advanced_stats_table <- advanced_stats_table %>%
  #  filter(Player != "Player")
  
  return(advanced_stats_table)
}

# Example usage
year <- 2018  # Specify the year
nba_advanced_stats <- get_nba_advanced_stats(year)

# Print the first few rows of the advanced stats
head(nba_advanced_stats)
NA
NA

combined_nba_stats<-function(year){
get_nba_roster2 <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)


  # Extract the table containing the player statistics
  roster_table <- webpage %>%
    html_node("table#per_game_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
  roster_table <- roster_table %>%
    filter(Player != "Player")
    return(roster_table)
}
  
  year <- 2023  # Specify the year
nba_roster2 <- get_nba_roster2(year)

#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)


#take out the N/A 
nba_roster2<-na.omit(nba_roster2)


# Convert specific columns from character to double

nba_roster2 %>%
   mutate(across(G:PTS, as.numeric))

#ADVANCED STATS

# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)
  
  # Extract the table containing the advanced player statistics
  advanced_stats_table <- webpage %>%
    html_node("table#advanced_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
 # advanced_stats_table <- advanced_stats_table %>%
  #  filter(Player != "Player")
  
  return(advanced_stats_table)
}

# Example usage
year <- 2023  # Specify the year
nba_advanced_stats2<- get_nba_advanced_stats(year)

# Print the first few rows of the advanced stats
head(nba_advanced_stats2)



#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats2<- nba_advanced_stats2[order(nba_advanced_stats2$Player),]





#remove na from dataframe
AO_nba_advanced_stats2 %>% 
  select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-20]
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-24]


# remove filler rows that had been previously used as headers on webpage


AO_nba_advanced_stats2 <- AO_nba_advanced_stats2[AO_nba_advanced_stats2$Player != "Player",]

AO_nba_advanced_stats2$Player <- factor(AO_nba_advanced_stats2$Player)




#change range of cloumns <dbl> from <chr>

AO_nba_advanced_stats2 %>%
   mutate(across(G:VORP, as.numeric))
   
   nba_merge<-merge(nba_roster2, AO_nba_advanced_stats2, by.x = c("Rk", "Player", "Pos","Age", "Tm","G"), by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
   
}

Example usage

year <- 2018  # Specify the year
nba_roster <- combined_nba_stats(year)
Error in fix.by(by.x, x) : 'by' must specify a uniquely valid column
year <- 1980  # Specify the year
nba_roster <- get_nba_roster(year)

#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
NA
nba_roster1<-nba_roster
nba_roster1$SGS<-nba_roster1$PTS+nba_roster1$TRB+nba_roster1$AST
nba_roster1$SGSind<-nba_roster1$SGS>24.3
head(nba_roster1)
NA
mean(nba_roster1$SGS>24.3,na.rm=T)
[1] 0.2016807
#Summary statistics

position_roster<-filter(nba_roster,Pos!="PG" )
position_roster

# Assuming 'nba_roster' is your data frame
input <- nba_roster1[, c('MP', 'PTS', 'Player','Pos','SGS','SGSind')]
input <- na.omit(input)
# Create the plotly scatter plot
fig <- plot_ly(input, x = ~MP, y = ~PTS, type = 'scatter', mode = 'markers',
               text = ~Player,  # This adds player names on hover
               hoverinfo = c('text',~MP), # Ensures that player names and data appear on hover
               color = ~SGSind,
               colors = c('green','red'),  # Colors points based on position
               marker = list(size = 10))

# Set the plot title and axis labels
fig <- fig %>% layout(title = "Minutes Played vs Points Scored",
                      xaxis = list(title = "Minutes Played", range = c(0, 48)),
                      yaxis = list(title = "Points", range = c(0, 35)))

fit<-lm(PTS~poly(MP,2),data=input)

# Add the best fit line to the plot
fig <- fig %>% add_lines(x = ~MP, 
                         y = fitted(fit), 
                         line = list(color = 'black'),
                         name = 'Best Fit Line')

# Show the plot
fig
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
saveWidget(fig, "MinPlayed_vs_PointScored.html")
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
#Data Visualization for Field Goals Attempled vs Field Goals Made

# Get the input values.
input_2 <- nba_roster1[, c('Player', 'MP', 'FGA', 'FG', 'SGS', 'SGSind')]

#input_2 <- na.omit(input_2)
# Set limits based on the data
b_FG <- max(input_2$FG, na.rm = TRUE)
b_FGA <- max(input_2$FGA, na.rm = TRUE)

# Create the plotly scatter plot
fig <- plot_ly(data = input_2, 
               x = ~FGA, 
               y = ~FG, 
               type = 'scatter', 
               mode = 'markers',
               marker = list(size = 10),
               text=~Player,
               hoverinfo = c('text',~MP), # Ensures that player names and data appear on hover
               color = ~SGSind,
               colors = c('green','red'))

# Customize the layout
fig <- fig %>% layout(title = 'Field Goal Attempts vs Field Goals Made',
                      xaxis = list(title = 'Field Goal Attempts', range = c(0.0, b_FGA)),
                      yaxis = list(title = 'Field Goals Made', range = c(0.0, b_FG)))

fit<-lm(FG~FGA,data=input_2)

# Add the best fit line to the plot
fig <- fig %>% add_lines(x = ~FGA, 
                         y = fitted(fit), 
                         line = list(color = 'black'),
                         name = 'Best Fit Line')

# Display the plot
fig
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
saveWidget(fig, "FGA_vs_FGMade.html")
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...
A marker object has been specified, but markers is not in the mode
Adding markers to the mode...

# Assuming 'nba_roster' is your data frame
input <- nba_roster1[, c('MP', 'PTS', 'Player','Pos','SGS','SGSind')]
#input <- na.omit(input)

input_3 <- na.omit(input)
# Set limits based on the data
b_SGS <- max(input_2$SGS, na.rm = TRUE)+5

# Create the plotly scatter plot
fig <- plot_ly(input, x = ~MP, y = ~SGS, type = 'scatter', mode = 'markers',
               text = ~Player,  # This adds player names on hover
               hoverinfo = c('text',~SGS), # Ensures that player names and data appear on hover
               color = ~SGSind,
               colors = c('green','red'),  # Colors points based on position
               marker = list(size = 10))

# Set the plot title and axis labels
fig <- fig %>% layout(title = "Minutes Played vs Simple Game Score",
                      xaxis = list(title = "Minutes Played", range = c(0, 48)),
                      yaxis = list(title = "Simple Game Score", range = c(0, b_SGS)))

#fit<-lm(SGS~poly(MP,2),data=input)

# Add the best fit line to the plot
#fig <- fig %>% add_lines(x = ~MP, 
 #                        y = fitted(fit), 
  #                       line = list(color = 'black'),
   #                      name = 'Best Fit Line')

# Show the plot
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
#saveWidget(fig, "MinPlayed_vs_SimpleGameScore.html")
summary(lm(FG~FGA,data=nba_roster1))

Call:
lm(formula = FG ~ FGA, data = nba_roster1)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5034 -0.2733 -0.0427  0.2415  1.8839 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.056479   0.030837  -1.832   0.0675 .  
FGA          0.458524   0.003893 117.773   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4483 on 662 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.9544,    Adjusted R-squared:  0.9544 
F-statistic: 1.387e+04 on 1 and 662 DF,  p-value: < 2.2e-16
nba_roster1E<-nba_roster1[which(nba_roster1$SGSind==T),]
summary(lm(FG~FGA,data=nba_roster1E))

Call:
lm(formula = FG ~ FGA, data = nba_roster1E)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.9539 -0.4270 -0.1272  0.3286  1.6776 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.28971    0.36287   3.554 0.000719 ***
FGA          0.39030    0.02355  16.571  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5992 on 64 degrees of freedom
Multiple R-squared:  0.811, Adjusted R-squared:  0.808 
F-statistic: 274.6 on 1 and 64 DF,  p-value: < 2.2e-16
nba_roster1NE<-nba_roster1[which(nba_roster1$SGSind==F),]
summary(lm(FG~FGA,data=nba_roster1NE))

Call:
lm(formula = FG ~ FGA, data = nba_roster1NE)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.41308 -0.24614 -0.03911  0.24207  1.76220 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.02751    0.03112   0.884    0.377    
FGA          0.43727    0.00472  92.639   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4022 on 596 degrees of freedom
Multiple R-squared:  0.9351,    Adjusted R-squared:  0.935 
F-statistic:  8582 on 1 and 596 DF,  p-value: < 2.2e-16
gg <- ggplot(data = nba_roster1 ) +  
 geom_density(aes(x=FG)) + geom_density(aes(x=FG, color=SGSind)) + geom_rug(aes(x=FG, color=SGSind)) + 
  ylab("") + 
  xlab("")

ggplotly(gg)%>% 
  layout(plot_bgcolor='#e5ecf6',   
             xaxis = list(   
               title='Time', 
               zerolinecolor = '#ffff',   
               zerolinewidth = 2,   
               gridcolor = 'ffff'),   
             yaxis = list(   
               title='Value A', 
               zerolinecolor = '#ffff',   
               zerolinewidth = 2,   
               gridcolor = 'ffff'),
         title = 'Curve and Rug Plot') 
Warning: Removed 1 row containing non-finite outside the scale range (`stat_density()`).
Warning: Removed 1 row containing non-finite outside the scale range (`stat_density()`).
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwbG90bHkpCmBgYAoKRnVuY3Rpb24gdG8gZ2V0IE5CQSByb3N0ZXIgZm9yIGEgc3BlY2lmaWVkIHllYXIKYGBge3J9CmdldF9uYmFfcm9zdGVyIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9wZXJfZ2FtZS5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCgoKICAjIEV4dHJhY3QgdGhlIHRhYmxlIGNvbnRhaW5pbmcgdGhlIHBsYXllciBzdGF0aXN0aWNzCiAgcm9zdGVyX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI3Blcl9nYW1lX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogIHJvc3Rlcl90YWJsZSA8LSByb3N0ZXJfdGFibGVbLXdoaWNoKHJvc3Rlcl90YWJsZSRQbGF5ZXI9PSdMZWFndWUgQXZlcmFnZScpLF0KICAjIENsZWFuIHRoZSBkYXRhIChyZW1vdmUgaGVhZGVyIHJvd3MgdGhhdCBtaWdodCBiZSBkdXBsaWNhdGVkKQogIHJvc3Rlcl90YWJsZSA8LSByb3N0ZXJfdGFibGUgJT4lCiAgICBmaWx0ZXIoUGxheWVyICE9ICJQbGF5ZXIiKQogICAgcmV0dXJuKHJvc3Rlcl90YWJsZSkKfQpgYGAKCgoKYGBge3J9CgojIEZ1bmN0aW9uIHRvIGdldCBOQkEgYWR2YW5jZWQgc3RhdHMgZm9yIGEgc3BlY2lmaWVkIHllYXIKZ2V0X25iYV9hZHZhbmNlZF9zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgIyBDb25zdHJ1Y3QgdGhlIFVSTCBmb3IgdGhlIHNwZWNpZmllZCB5ZWFyCiAgdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2xlYWd1ZXMvTkJBXyIsIHllYXIsICJfYWR2YW5jZWQuaHRtbCIpCiAgCiAgIyBSZWFkIHRoZSBIVE1MIGNvbnRlbnQgZnJvbSB0aGUgVVJMCiAgd2VicGFnZSA8LSByZWFkX2h0bWwodXJsKQogIAogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgYWR2YW5jZWQgcGxheWVyIHN0YXRpc3RpY3MKICBhZHZhbmNlZF9zdGF0c190YWJsZSA8LSB3ZWJwYWdlICU+JQogICAgaHRtbF9ub2RlKCJ0YWJsZSNhZHZhbmNlZF9zdGF0cyIpICU+JQogICAgaHRtbF90YWJsZShmaWxsID0gVFJVRSkKICAKICAjIENsZWFuIHRoZSBkYXRhIChyZW1vdmUgaGVhZGVyIHJvd3MgdGhhdCBtaWdodCBiZSBkdXBsaWNhdGVkKQogIyBhZHZhbmNlZF9zdGF0c190YWJsZSA8LSBhZHZhbmNlZF9zdGF0c190YWJsZSAlPiUKICAjICBmaWx0ZXIoUGxheWVyICE9ICJQbGF5ZXIiKQogIAogIHJldHVybihhZHZhbmNlZF9zdGF0c190YWJsZSkKfQoKIyBFeGFtcGxlIHVzYWdlCnllYXIgPC0gMjAxOCAgIyBTcGVjaWZ5IHRoZSB5ZWFyCm5iYV9hZHZhbmNlZF9zdGF0cyA8LSBnZXRfbmJhX2FkdmFuY2VkX3N0YXRzKHllYXIpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgYWR2YW5jZWQgc3RhdHMKaGVhZChuYmFfYWR2YW5jZWRfc3RhdHMpCgoKYGBgCgpgYGB7cn0KCmNvbWJpbmVkX25iYV9zdGF0czwtZnVuY3Rpb24oeWVhcil7CmdldF9uYmFfcm9zdGVyMiA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgIyBDb25zdHJ1Y3QgdGhlIFVSTCBmb3IgdGhlIHNwZWNpZmllZCB5ZWFyCiAgdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2xlYWd1ZXMvTkJBXyIsIHllYXIsICJfcGVyX2dhbWUuaHRtbCIpCiAgCiAgIyBSZWFkIHRoZSBIVE1MIGNvbnRlbnQgZnJvbSB0aGUgVVJMCiAgd2VicGFnZSA8LSByZWFkX2h0bWwodXJsKQoKCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBwbGF5ZXIgc3RhdGlzdGljcwogIHJvc3Rlcl90YWJsZSA8LSB3ZWJwYWdlICU+JQogICAgaHRtbF9ub2RlKCJ0YWJsZSNwZXJfZ2FtZV9zdGF0cyIpICU+JQogICAgaHRtbF90YWJsZShmaWxsID0gVFJVRSkKICAKICAjIENsZWFuIHRoZSBkYXRhIChyZW1vdmUgaGVhZGVyIHJvd3MgdGhhdCBtaWdodCBiZSBkdXBsaWNhdGVkKQogIHJvc3Rlcl90YWJsZSA8LSByb3N0ZXJfdGFibGUgJT4lCiAgICBmaWx0ZXIoUGxheWVyICE9ICJQbGF5ZXIiKQogICAgcmV0dXJuKHJvc3Rlcl90YWJsZSkKfQogIAogIHllYXIgPC0gMjAyMyAgIyBTcGVjaWZ5IHRoZSB5ZWFyCm5iYV9yb3N0ZXIyIDwtIGdldF9uYmFfcm9zdGVyMih5ZWFyKQoKI1ByaW50IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgcm9zdGVyCmhlYWQobmJhX3Jvc3RlcikKdGFpbChuYmFfcm9zdGVyKQoKCiN0YWtlIG91dCB0aGUgTi9BIApuYmFfcm9zdGVyMjwtbmEub21pdChuYmFfcm9zdGVyMikKCgojIENvbnZlcnQgc3BlY2lmaWMgY29sdW1ucyBmcm9tIGNoYXJhY3RlciB0byBkb3VibGUKCm5iYV9yb3N0ZXIyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6UFRTLCBhcy5udW1lcmljKSkKCiNBRFZBTkNFRCBTVEFUUwoKIyBGdW5jdGlvbiB0byBnZXQgTkJBIGFkdmFuY2VkIHN0YXRzIGZvciBhIHNwZWNpZmllZCB5ZWFyCmdldF9uYmFfYWR2YW5jZWRfc3RhdHMgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX2FkdmFuY2VkLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKICAKICAjIEV4dHJhY3QgdGhlIHRhYmxlIGNvbnRhaW5pbmcgdGhlIGFkdmFuY2VkIHBsYXllciBzdGF0aXN0aWNzCiAgYWR2YW5jZWRfc3RhdHNfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjYWR2YW5jZWRfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICMgYWR2YW5jZWRfc3RhdHNfdGFibGUgPC0gYWR2YW5jZWRfc3RhdHNfdGFibGUgJT4lCiAgIyAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAKICByZXR1cm4oYWR2YW5jZWRfc3RhdHNfdGFibGUpCn0KCiMgRXhhbXBsZSB1c2FnZQp5ZWFyIDwtIDIwMjMgICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfYWR2YW5jZWRfc3RhdHMyPC0gZ2V0X25iYV9hZHZhbmNlZF9zdGF0cyh5ZWFyKQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIGFkdmFuY2VkIHN0YXRzCmhlYWQobmJhX2FkdmFuY2VkX3N0YXRzMikKCgoKI3dhbnQgdG8gb3JkZXIgYnkgYWxwaGFiZXRpYyBuYW1lIHRvIG1ha2UgY2xlYW5pbmcgb3V0IHRoZSBmaWxsZXIgaGVhZGVycyBmcm9tIHRoZSBkYXRhc2V0CkFPX25iYV9hZHZhbmNlZF9zdGF0czI8LSBuYmFfYWR2YW5jZWRfc3RhdHMyW29yZGVyKG5iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyKSxdCgoKCgoKI3JlbW92ZSBuYSBmcm9tIGRhdGFmcmFtZQpBT19uYmFfYWR2YW5jZWRfc3RhdHMyICU+JSAKICBzZWxlY3Qod2hlcmUofiFhbGwoaXMubmEoLikpKSkKI3JlbW92aW5nIGNvbHVtbiAyMCBhbmQgMjUgZnJvbSBkYXRhZnJhbWUgc2luY2UgdGhleXJlIGJsYW5rcwpBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC1BT19uYmFfYWR2YW5jZWRfc3RhdHMyWywtMjBdCkFPX25iYV9hZHZhbmNlZF9zdGF0czI8LUFPX25iYV9hZHZhbmNlZF9zdGF0czJbLC0yNF0KCgojIHJlbW92ZSBmaWxsZXIgcm93cyB0aGF0IGhhZCBiZWVuIHByZXZpb3VzbHkgdXNlZCBhcyBoZWFkZXJzIG9uIHdlYnBhZ2UKCgpBT19uYmFfYWR2YW5jZWRfc3RhdHMyIDwtIEFPX25iYV9hZHZhbmNlZF9zdGF0czJbQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiRQbGF5ZXIgIT0gIlBsYXllciIsXQoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiRQbGF5ZXIgPC0gZmFjdG9yKEFPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyKQoKCgoKI2NoYW5nZSByYW5nZSBvZiBjbG91bW5zIDxkYmw+IGZyb20gPGNocj4KCkFPX25iYV9hZHZhbmNlZF9zdGF0czIgJT4lCiAgIG11dGF0ZShhY3Jvc3MoRzpWT1JQLCBhcy5udW1lcmljKSkKICAgCiAgIG5iYV9tZXJnZTwtbWVyZ2UobmJhX3Jvc3RlcjIsIEFPX25iYV9hZHZhbmNlZF9zdGF0czIsIGJ5LnggPSBjKCJSayIsICJQbGF5ZXIiLCAiUG9zIiwiQWdlIiwgIlRtIiwiRyIpLCBieS55ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSAsIGFsbC54ID0gVFJVRSwgYWxsLnkgPSBUUlVFKQogICAKfQoKCgpgYGAKCgoKRXhhbXBsZSB1c2FnZQoKYGBge3J9CnllYXIgPC0gMjAxOCAgIyBTcGVjaWZ5IHRoZSB5ZWFyCm5iYV9yb3N0ZXIgPC0gY29tYmluZWRfbmJhX3N0YXRzKHllYXIpCgojUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByb3N0ZXIKaGVhZChuYmFfcm9zdGVyKQp0YWlsKG5iYV9yb3N0ZXIpCgpgYGAKCmBgYHtyfQp5ZWFyIDwtIDE5ODAgICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyIDwtIGdldF9uYmFfcm9zdGVyKHllYXIpCgojUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByb3N0ZXIKaGVhZChuYmFfcm9zdGVyKQp0YWlsKG5iYV9yb3N0ZXIpCgpgYGAKCmBgYHtyfQpuYmFfcm9zdGVyMTwtbmJhX3Jvc3RlcgpuYmFfcm9zdGVyMSRTR1M8LW5iYV9yb3N0ZXIxJFBUUytuYmFfcm9zdGVyMSRUUkIrbmJhX3Jvc3RlcjEkQVNUCm5iYV9yb3N0ZXIxJFNHU2luZDwtbmJhX3Jvc3RlcjEkU0dTPjI0LjMKaGVhZChuYmFfcm9zdGVyMSkKCmBgYAoKCgpgYGB7cn0KbWVhbihuYmFfcm9zdGVyMSRTR1M+MjQuMyxuYS5ybT1UKQpgYGAKCgoKYGBge3J9CiNTdW1tYXJ5IHN0YXRpc3RpY3MKCnBvc2l0aW9uX3Jvc3RlcjwtZmlsdGVyKG5iYV9yb3N0ZXIsUG9zIT0iUEciICkKcG9zaXRpb25fcm9zdGVyCmBgYAoKYGBge3J9CgojIEFzc3VtaW5nICduYmFfcm9zdGVyJyBpcyB5b3VyIGRhdGEgZnJhbWUKaW5wdXQgPC0gbmJhX3Jvc3RlcjFbLCBjKCdNUCcsICdQVFMnLCAnUGxheWVyJywnUG9zJywnU0dTJywnU0dTaW5kJyldCmlucHV0IDwtIG5hLm9taXQoaW5wdXQpCiMgQ3JlYXRlIHRoZSBwbG90bHkgc2NhdHRlciBwbG90CmZpZyA8LSBwbG90X2x5KGlucHV0LCB4ID0gfk1QLCB5ID0gflBUUywgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdtYXJrZXJzJywKICAgICAgICAgICAgICAgdGV4dCA9IH5QbGF5ZXIsICAjIFRoaXMgYWRkcyBwbGF5ZXIgbmFtZXMgb24gaG92ZXIKICAgICAgICAgICAgICAgaG92ZXJpbmZvID0gYygndGV4dCcsfk1QKSwgIyBFbnN1cmVzIHRoYXQgcGxheWVyIG5hbWVzIGFuZCBkYXRhIGFwcGVhciBvbiBob3ZlcgogICAgICAgICAgICAgICBjb2xvciA9IH5TR1NpbmQsCiAgICAgICAgICAgICAgIGNvbG9ycyA9IGMoJ2dyZWVuJywncmVkJyksICAjIENvbG9ycyBwb2ludHMgYmFzZWQgb24gcG9zaXRpb24KICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMTApKQoKIyBTZXQgdGhlIHBsb3QgdGl0bGUgYW5kIGF4aXMgbGFiZWxzCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkIiwKICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJNaW51dGVzIFBsYXllZCIsIHJhbmdlID0gYygwLCA0OCkpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlBvaW50cyIsIHJhbmdlID0gYygwLCAzNSkpKQoKZml0PC1sbShQVFN+cG9seShNUCwyKSxkYXRhPWlucHV0KQoKIyBBZGQgdGhlIGJlc3QgZml0IGxpbmUgdG8gdGhlIHBsb3QKZmlnIDwtIGZpZyAlPiUgYWRkX2xpbmVzKHggPSB+TVAsIAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZpdHRlZChmaXQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJyksCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ0Jlc3QgRml0IExpbmUnKQoKIyBTaG93IHRoZSBwbG90CmZpZwoKc2F2ZVdpZGdldChmaWcsICJNaW5QbGF5ZWRfdnNfUG9pbnRTY29yZWQuaHRtbCIpCgpgYGAKCgpgYGB7cn0KI0RhdGEgVmlzdWFsaXphdGlvbiBmb3IgRmllbGQgR29hbHMgQXR0ZW1wbGVkIHZzIEZpZWxkIEdvYWxzIE1hZGUKCiMgR2V0IHRoZSBpbnB1dCB2YWx1ZXMuCmlucHV0XzIgPC0gbmJhX3Jvc3RlcjFbLCBjKCdQbGF5ZXInLCAnTVAnLCAnRkdBJywgJ0ZHJywgJ1NHUycsICdTR1NpbmQnKV0KCiNpbnB1dF8yIDwtIG5hLm9taXQoaW5wdXRfMikKIyBTZXQgbGltaXRzIGJhc2VkIG9uIHRoZSBkYXRhCmJfRkcgPC0gbWF4KGlucHV0XzIkRkcsIG5hLnJtID0gVFJVRSkKYl9GR0EgPC0gbWF4KGlucHV0XzIkRkdBLCBuYS5ybSA9IFRSVUUpCgojIENyZWF0ZSB0aGUgcGxvdGx5IHNjYXR0ZXIgcGxvdApmaWcgPC0gcGxvdF9seShkYXRhID0gaW5wdXRfMiwgCiAgICAgICAgICAgICAgIHggPSB+RkdBLCAKICAgICAgICAgICAgICAgeSA9IH5GRywgCiAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIAogICAgICAgICAgICAgICBtb2RlID0gJ21hcmtlcnMnLAogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgIHRleHQ9flBsYXllciwKICAgICAgICAgICAgICAgaG92ZXJpbmZvID0gYygndGV4dCcsfk1QKSwgIyBFbnN1cmVzIHRoYXQgcGxheWVyIG5hbWVzIGFuZCBkYXRhIGFwcGVhciBvbiBob3ZlcgogICAgICAgICAgICAgICBjb2xvciA9IH5TR1NpbmQsCiAgICAgICAgICAgICAgIGNvbG9ycyA9IGMoJ2dyZWVuJywncmVkJykpCgojIEN1c3RvbWl6ZSB0aGUgbGF5b3V0CmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICdGaWVsZCBHb2FsIEF0dGVtcHRzIHZzIEZpZWxkIEdvYWxzIE1hZGUnLAogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ0ZpZWxkIEdvYWwgQXR0ZW1wdHMnLCByYW5nZSA9IGMoMC4wLCBiX0ZHQSkpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0ZpZWxkIEdvYWxzIE1hZGUnLCByYW5nZSA9IGMoMC4wLCBiX0ZHKSkpCgpmaXQ8LWxtKEZHfkZHQSxkYXRhPWlucHV0XzIpCgojIEFkZCB0aGUgYmVzdCBmaXQgbGluZSB0byB0aGUgcGxvdApmaWcgPC0gZmlnICU+JSBhZGRfbGluZXMoeCA9IH5GR0EsIAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZpdHRlZChmaXQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJyksCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ0Jlc3QgRml0IExpbmUnKQoKIyBEaXNwbGF5IHRoZSBwbG90CmZpZwoKc2F2ZVdpZGdldChmaWcsICJGR0FfdnNfRkdNYWRlLmh0bWwiKQpgYGAKCgpgYGB7cn0KCiMgQXNzdW1pbmcgJ25iYV9yb3N0ZXInIGlzIHlvdXIgZGF0YSBmcmFtZQppbnB1dCA8LSBuYmFfcm9zdGVyMVssIGMoJ01QJywgJ1BUUycsICdQbGF5ZXInLCdQb3MnLCdTR1MnLCdTR1NpbmQnKV0KI2lucHV0IDwtIG5hLm9taXQoaW5wdXQpCgppbnB1dF8zIDwtIG5hLm9taXQoaW5wdXQpCiMgU2V0IGxpbWl0cyBiYXNlZCBvbiB0aGUgZGF0YQpiX1NHUyA8LSBtYXgoaW5wdXRfMiRTR1MsIG5hLnJtID0gVFJVRSkrNQoKIyBDcmVhdGUgdGhlIHBsb3RseSBzY2F0dGVyIHBsb3QKZmlnIDwtIHBsb3RfbHkoaW5wdXQsIHggPSB+TVAsIHkgPSB+U0dTLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLAogICAgICAgICAgICAgICB0ZXh0ID0gflBsYXllciwgICMgVGhpcyBhZGRzIHBsYXllciBuYW1lcyBvbiBob3ZlcgogICAgICAgICAgICAgICBob3ZlcmluZm8gPSBjKCd0ZXh0Jyx+U0dTKSwgIyBFbnN1cmVzIHRoYXQgcGxheWVyIG5hbWVzIGFuZCBkYXRhIGFwcGVhciBvbiBob3ZlcgogICAgICAgICAgICAgICBjb2xvciA9IH5TR1NpbmQsCiAgICAgICAgICAgICAgIGNvbG9ycyA9IGMoJ2dyZWVuJywncmVkJyksICAjIENvbG9ycyBwb2ludHMgYmFzZWQgb24gcG9zaXRpb24KICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMTApKQoKIyBTZXQgdGhlIHBsb3QgdGl0bGUgYW5kIGF4aXMgbGFiZWxzCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJNaW51dGVzIFBsYXllZCB2cyBTaW1wbGUgR2FtZSBTY29yZSIsCiAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiTWludXRlcyBQbGF5ZWQiLCByYW5nZSA9IGMoMCwgNDgpKSwKICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJTaW1wbGUgR2FtZSBTY29yZSIsIHJhbmdlID0gYygwLCBiX1NHUykpKQoKI2ZpdDwtbG0oU0dTfnBvbHkoTVAsMiksZGF0YT1pbnB1dCkKCiMgQWRkIHRoZSBiZXN0IGZpdCBsaW5lIHRvIHRoZSBwbG90CiNmaWcgPC0gZmlnICU+JSBhZGRfbGluZXMoeCA9IH5NUCwgCiAjICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZpdHRlZChmaXQpLCAKICAjICAgICAgICAgICAgICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycpLAogICAjICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnQmVzdCBGaXQgTGluZScpCgojIFNob3cgdGhlIHBsb3QKZmlnCgojc2F2ZVdpZGdldChmaWcsICJNaW5QbGF5ZWRfdnNfU2ltcGxlR2FtZVNjb3JlLmh0bWwiKQoKCmBgYAoKCgoKYGBge3J9CnN1bW1hcnkobG0oRkd+RkdBLGRhdGE9bmJhX3Jvc3RlcjEpKQpgYGAKYGBge3J9Cm5iYV9yb3N0ZXIxRTwtbmJhX3Jvc3RlcjFbd2hpY2gobmJhX3Jvc3RlcjEkU0dTaW5kPT1UKSxdCnN1bW1hcnkobG0oRkd+RkdBLGRhdGE9bmJhX3Jvc3RlcjFFKSkKYGBgCmBgYHtyfQpuYmFfcm9zdGVyMU5FPC1uYmFfcm9zdGVyMVt3aGljaChuYmFfcm9zdGVyMSRTR1NpbmQ9PUYpLF0Kc3VtbWFyeShsbShGR35GR0EsZGF0YT1uYmFfcm9zdGVyMU5FKSkKYGBgCgoKYGBge3J9CmdnIDwtIGdncGxvdChkYXRhID0gbmJhX3Jvc3RlcjEgKSArICAKIGdlb21fZGVuc2l0eShhZXMoeD1GRykpICsgZ2VvbV9kZW5zaXR5KGFlcyh4PUZHLCBjb2xvcj1TR1NpbmQpKSArIGdlb21fcnVnKGFlcyh4PUZHLCBjb2xvcj1TR1NpbmQpKSArIAogIHlsYWIoIiIpICsgCiAgeGxhYigiIikKCmdncGxvdGx5KGdnKSU+JSAKICBsYXlvdXQocGxvdF9iZ2NvbG9yPScjZTVlY2Y2JywgICAKICAgICAgICAgICAgIHhheGlzID0gbGlzdCggICAKICAgICAgICAgICAgICAgdGl0bGU9J1RpbWUnLCAKICAgICAgICAgICAgICAgemVyb2xpbmVjb2xvciA9ICcjZmZmZicsICAgCiAgICAgICAgICAgICAgIHplcm9saW5ld2lkdGggPSAyLCAgIAogICAgICAgICAgICAgICBncmlkY29sb3IgPSAnZmZmZicpLCAgIAogICAgICAgICAgICAgeWF4aXMgPSBsaXN0KCAgIAogICAgICAgICAgICAgICB0aXRsZT0nVmFsdWUgQScsIAogICAgICAgICAgICAgICB6ZXJvbGluZWNvbG9yID0gJyNmZmZmJywgICAKICAgICAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsICAgCiAgICAgICAgICAgICAgIGdyaWRjb2xvciA9ICdmZmZmJyksCiAgICAgICAgIHRpdGxlID0gJ0N1cnZlIGFuZCBSdWcgUGxvdCcpIApgYGAKCg==